最佳 Go 框架 = 没有框架?
武功是无招胜有招,太极是四两破千斤,而Go是无框胜有框,为什么?Let‘sGo!
在领导 Go 研发团队的几年里,我从初学者那里听到的最常见问题是:“我该用什么框架?”
其实,在Go中你能做的最烂的事情之一就是遵循其它编程语言的方法。
在其它语言中,已经建立了“默认”框架。Java有Spring,Python有Django和Flask,Ruby有Rails,C#有ASP.NET,Node有Express,PHP有Symfony和Laravel。
而 Go 则与众不同:没有自己的框架!
更有趣的是,许多使用Go的人建议你根本不应该使用框架。难道他们是真疯了吗?
从库中构建Go服务会给人一种构造弗兰肯斯坦怪物的感觉
Unix 哲学
其实也是存在 Go 框架的,但没有一个提供像其它语言框架那样的功能集,这个哲学不会很快发生改变。
你可能认为这是因为 Go 生态系统更年轻,但还有一个更重要的因素。Go 是围绕 Unix 哲学构建的,它是这样式儿说的:
编写只做好一件事的程序。
编写程序是用来协同工作。
编写程序来处理文本流,因为这是一个通用接口。
这种哲学起源于B 语言(C语言的前身)的设计者KenThompson,还有Go!
实际上,Unix 哲学倾向于构建独立的小软件来做好一件事,而不是构建做所有事情的大块。
各位可能已经在自己的终端中看到这些小东东。比如:cat example.txt | sort | uniq。cat从文件中读取文本,sort对行进行排序,用uniq删除重复项。这些命令都是独立的,它们只做一件事,直接来自 Unix 哲学。就是因为这样的设计,用户可以独立开发更小的、属于自主的命令行应用。
在Go中,Unix哲学在标准库中随处可见。最好的例子是最广泛使用的接口:theio.Reader和io.Writer,它里面最好的库也是遵循这一理念。
而软件框架也是针对这种理念设计的。通常情况下,作者试图在一个框架内涵盖所有可能的用例。它们不是为与其它工具一起使用而设计的,而是目标不能重复使用。这意味着不可能将开发工作转移到其它不兼容的框架。如果你的框架采用率很低(或者很快就死了),那么所有的努力就算白费。
什么对你很重要
每个技术决策都有权衡。你需要选择对自己和你的项目做更有意义的权衡。
当您进行一个概念验证然后将其扔掉时,某些方法是有意义的。在那种情况下,最关键的因素是你能多久完成它。但是,如果我们从事一个将持续很长时间的项目,并且与多人一起开发,那么该决定的影响是巨大的。
对于大多数项目,最重要的参数是:
你能多快开始这个项目,
从长远来看,你能以多快的速度开发这个项目
项目对未来变化的灵活性(与前一点密切相关)
让我们开始评估一些决策。
节省时间
框架给出我们的最大承诺之一是节省时间。
运行一个命令,你就得到了一个功能齐全的项目。框架通常提供项目的固定架构,如果还不知道该怎么做,它会提供帮助信息,与大多数其他技术决策一样,它并不是完全免费的。
随着时间的推移,当项目增长时,您将很快碰到框架的约定和限制。该框架的作者要求可能与自己的不一样。框架创建者做出的决定可能适用于简单的 CRUD 应用程序,但可能无法处理更复杂的场景。 为了与一个框架限制作斗争,很容易很快就浪费掉在项目引导程序上节省的所有时间。
随着时间的推移,它可能会给你的团队带来不少挫败感。
几年前,我在一家最初使用一个Go框架起家的公司工作(我跳过使用该框架的名字)。公司正在快速发展并创造新的服务。随着时间的推移,当我们想要支持更复杂的用例时,我们开始感到痛苦了。
使用这个框架也是这个严重错误的来源。糟糕的是,摆脱框架并不太那么容易。
有一次,一些框架组件无人维护了,并且与生态系统的其它版本不兼容。我们被迫扔掉它,然后该框架已经非常紧密耦合到整个系统。从数十个服务中删除它变成一项非常重要的任务。它需要一个跨团队的建议,需要花费多个人/月和一些会议才能搞定。即使最后项目成功了,我也不怎么看好。 如果有人早点做出不同的决定,那么所花费的时间都可以得到更好的利用,不需要涉及到整个项目。
当然,许多公司对开发团队缺乏信任,这种情况也并不奇怪。
下面是一个很好的例子,从一个小的决策开始,如何在几年后成为一项代价高昂的救火项目的。
项目的可维护
衡量项目的可维护性是一个有争议的话题——比较两个项目是个挺困难的事情。
有人说框架很棒,用起来感觉很爽。但对于有的人来说,还有从长远来看,框架可能是比较大的噩梦,有的框架比项目更具挑战性。很多人认为和框架打架也是工作一部分,这就是为什么,客观衡量框架对项目可维护性的影响是有挑战的事。
幸运的是,我们可以通过一些科学手段来自我剖析它。更准确地说,可以基于此项科学研究的精华——《加速:精益软件科学和 DevOps》一书。
这本书重点是找出表现最好和最差的团队特征。对我来说重点是如何获得良好性能,而最重要因素之一就是松散耦合的架构。
我的团队经常问如何知道架构是否松散耦合。
最简单的方法是确保可以轻松替换或删除应用程序的某一部分。 如果很难删除应用程序的某些部分,则说明你的应用程序是紧密耦合的。触动一发,动其全身,再加异地变化,那么就必然形成多米诺骨牌效应!
为什么松耦合架构这么重要?让我们承认吧,我们是人类,即使经过最好的研究,我们也会犯错误。
当你选择了不对的框架或库时,应该做到很容易替换它而不是重写整个项目。如果我们想节省时间,应该考虑从长远来看有什么帮助,而不仅仅是在项目开始时:
考虑一个场景,当您想要完全删除框架时,它需要大量的代码重写吗?它可以独立地在多个服务上完成吗?如果没有,我们要努力将框架与核心逻辑分开,但这些首先需要牺牲它提供的“时间节约”。
替代方案?在没有框架的情况下构建服务
很多人可能觉得,在没有框架的情况下构建服务会花费很长时间。特别是如果一些人来自其它编程语言。我是明白这些的,几年前当我开始用 Go 编写代码时,我也有过同样的感觉,但这是一种毫无根据的恐惧。不使用框架并不意味着你需要自己构建所有内容。有许多经过验证的库可以提供所需要的功能。
你需要在研究上付出更多的努力,包括阅读本文!
在整个项目的生命周期中,即使花十几个小时的研究也不算什么。由于它为我们提供的灵活性,我们也将很快回到那个时间。
如果你决定不使用框架,应该怎么做?一开始最大的障碍可能是如何构建服务。 最简单的方法是从将所有内容放入一个文件开始。可以从最简单开始,推迟一些决定,并随着时间,改进自己的项目。
示例项目会很有帮助。你可以查看我在GoRemoteFest上用 Watermill 上,用 15分钟内构建事件驱动应用时的项目:github.com/roblaszczak/goremotefest-livecoding。这个例子只需两个外部库就可以正常跑起来。
你可以克隆此存储库,并根据自己的需要采用,但肯定没有你项目所需的所有库。为了帮助您处理更具体的用例,下周我还将发布一篇文章,其中包含可用于构建您的 Go 服务的 Go 库列表。我们已经使用它们几年了。我还将解释为什么我们使用这些库,以及如何知道类似的库是好是坏。
当你的项目开始变得更加复杂时,如果你已经知道让库如何协同工作,不妨开始重构它。
最后,你不需要大多数看起来很重要的框架功能,最终你可以得到一个更简单的项目和更少的研发。
总结
决定如何构建服务,不是你应该走捷径的地方。从长远来看,做出错误的决定会对自己和团队产生负面的影响,尤其会对团队的速度产生负面影响,影响士气。
做出错误的决定后,你很快就会陷入沉没成本的谬误陷阱。我们不应成为解决别人制造问题的英雄,而应该全力避免制造它们。
在本篇文章的末尾,相信各位应该了解一些方法的权衡与含义,你现在可以做出负责任的一些决策。
我希望这篇文章至少能帮助一家公司避免几个人几个月的痛苦项目重构。你是否有相关框架造成的故事?可以在评论区中让更多人知道。
编译:洛逸
作者:Robert Laszczak
来源:
https://threedots.tech/post/best-go-framework/
相关阅读: